package helper.scxml.strategy.sNodeUnit

import helper.base.A2LHM
import helper.base.A2Tuple
import helper.base.LHMHelper.A2LHMExpand.add
import helper.base.LHMHelper.A2LHMExpand.addList
import helper.base.LHMHelper.A2LHMExpand.makeA2LHM
import helper.base.LHMHelper.A3LHM
import helper.base.LHMHelper.A3LHM.Companion.makeA3LHM
import helper.base.LHMHelper.LHMExpand.toA2LHM
import helper.scxml.scxml2.zone.t2.ConfigHelper.Config
import helper.scxml.strategy.StrategyHelper.TimedEventHelper.RangeTimedEvent
import helper.scxml.strategy.StrategyHelper.TimedEventHelper.RangeTimedEvent.Companion.makeRangeTimedEvent
import helper.scxml.strategy.StrategyHelper.TimedEventHelper.TimedEvent
import helper.scxml.strategy.TotalStrategyHelper.ISNode
import helper.scxml.strategy.TotalStrategyHelper.KREWTuple
import helper.scxml.strategy.TotalStrategyHelper.LeafSNode
import helper.scxml.strategy.TotalStrategyHelper.LeafSNode.Companion.makeLeafSNode
import helper.scxml.strategy.TotalStrategyHelper.SNode
import helper.scxml.strategy.sNodeUnit.SNodeUnitHelper.ISNodeUnit

object T1SNodeUnitHelper {
    fun Array<KREWTuple>.addToT1SNodeUnit(
        sNodeUnit: T1SNodeUnit,
        fatherSNode: SNode,
    ) {
        val a2LHM = makeA2LHM<String, ArrayList<A2Tuple<RangeTimedEvent, Double>>>()
        this.forEach { (a1, a2, a3, a4) ->
            a2LHM.addList(
                a1, arrayListOf(
                    makeRangeTimedEvent(a2, a3) to a4
                )
            )
        }
        a2LHM.forEach { (a1, a2) ->
            sNodeUnit.relationLHM.add(
                fatherSNode,
                a1,
                makeLeafSNode(a2.toA2LHM()),
            )
        }
    }

    class T1SNodeUnit(
        val root: ISNode,
        val funLHM: A2LHM<ISNode, (Config) -> String>,
        val relationLHM: A3LHM<ISNode, String, ISNode>,
    ) : ISNodeUnit {
        companion object {
            fun newT1SNodeUnit(
                root: ISNode,
                funLHM: A2LHM<ISNode, (Config) -> String> = makeA2LHM(),
                relationLHM: A3LHM<ISNode, String, ISNode> = makeA3LHM(),
            ): T1SNodeUnit {
                return T1SNodeUnit(
                    root,
                    funLHM,
                    relationLHM,
                )
            }
        }

        fun getLeafSNode(config: Config): LeafSNode {
            var nowSNode = root
            while (nowSNode !is LeafSNode) {
                val function = funLHM[nowSNode]!!
                nowSNode = relationLHM[nowSNode, function(config)]!!
            }
            return nowSNode
        }

        fun getMTimeEventChoice(
            config: Config,
        ): TimedEvent? {
            return getLeafSNode(
                config,
            ).getTimeEventChoice()
        }

        override fun getMsTimeEventChoiceList(
            config: Config,
        ): ArrayList<TimedEvent> {
            val res = arrayListOf<TimedEvent>()
            getMTimeEventChoice(config)?.let {
                res.add(it)
            }
            return res
        }

        fun getMTimeEventChoiceWeightLHM(
            config: Config,
        ): A2LHM<TimedEvent, Double> {
            return getLeafSNode(
                config,
            ).getTimeEventChoiceWeightLHM()
        }

        override fun getMsTimeEventChoiceWeightLHM(
            config: Config,
        ): A2LHM<List<TimedEvent>, Double> {
            val a2LHM = makeA2LHM<List<TimedEvent>, Double>()
            getMTimeEventChoiceWeightLHM(
                config,
            ).forEach { (t, u) ->
                a2LHM.add(
                    arrayListOf(t),
                    u,
                )
            }
            return a2LHM
        }
    }
}